#include "filechannel.h"

#include <iostream>
#include <unistd.h>
#include <sys/syscall.h>
#include <stdlib.h>

#include "cpputils.h"

using namespace std;

FileChannel::FileChannel(const string& prefpath,const string& preftime,
        const string& prefproc,bool prefms):
    m_pFile(0),
    m_path(prefpath),
    m_preftime(preftime),
    m_prefproc(prefproc),
    m_prefms(prefms),
    m_rotatesize(0),
    m_rtimeUnit(ROTATE_HOUR),
    m_rtimeCount(0)
{
    open();
    time_t tnow = time(NULL);
    struct tm tmnow;
    tmnow = *localtime(&tnow);
    tmnow.tm_sec = 0;
    tmnow.tm_min = 0;
    m_lastrtime = mktime(&tmnow);
}

FileChannel::~FileChannel()
{
    close();
}

string FileChannel::genFileName()
{
    string timenow = GetTime("_%Y%m%d_%H%M%S.log");
    return m_path + timenow;
}

void FileChannel::open()
{
    if (m_pFile != NULL)
        return;
    
    m_pFile = new std::fstream();
    string filename = genFileName();
    m_pFile->open(filename.c_str(),fstream::in | fstream::out | fstream::app);
    m_pFile->seekg(0,m_pFile->end);
    m_filesize = m_pFile->tellg();
}

void FileChannel::close()
{
    if (m_pFile)
        m_pFile->close();
    
    delete m_pFile;
    m_pFile = NULL;
}

void FileChannel::log(const LogMsg& msg)
{
    (*m_pFile)<< getPrefix(msg.m_prio,msg.m_tid) <<msg.m_file<<":"<<msg.m_line<<":"<<msg.m_msg<<"\n";
    
    rotate();
}

void FileChannel::rotate()
{
    if ( m_rotatesize > 0 )
    {
        m_pFile->seekg(0,m_pFile->end);
        m_filesize = m_pFile->tellg();
        
        if ( m_filesize >= m_rotatesize )
        {
            this->close();
            this->open();
            // 按大小滚动不更新滚动时间
            //m_lastrtime = time(NULL);
            return;
        }
    }

    if (m_rtimeCount > 0)
    {
        time_t tnow = time(NULL);
        double secs = difftime(tnow,m_lastrtime);
        switch ( m_rtimeUnit )
        {
            case ROTATE_HOUR:
            {
                long hour = secs/3600/m_rtimeCount;
                if (hour > 0 )
                {
                    goto TIME_ROTATE;
                }
                return;
            }
            case ROTATE_DAY:
            {
                long day = secs/3600/24/m_rtimeCount;
                if (day > 0 )
                {
                    goto TIME_ROTATE;
                }
                return;
            }
            case ROTATE_WEEK:
            {
                long week = secs/3600/24/7/m_rtimeCount;
                if ( week > 0 )
                {
                    goto TIME_ROTATE;
                }
                return;
            }
            case ROTATE_MONTH:
            {
                struct tm tmnow    = *localtime(&tnow);
                struct tm tmlast   = *localtime(&m_lastrtime);
                if ((unsigned int)(tmnow.tm_mon - tmlast.tm_mon + 12) > m_rtimeCount)
                {
                    goto TIME_ROTATE;
                }
                return;
            }
            default:
                return;
        }

    TIME_ROTATE:
        this->close();
        this->open();
        m_lastrtime = tnow;
    }
}

void FileChannel::setRotateSize(const string& rsize)
{
    /// 格式 1K,100M,1G
    string tmpsize = to_lower(rsize);
    if ( tmpsize.length() > 0)
    {
        long size = atoi(rsize.c_str());
        switch( tmpsize[tmpsize.length() - 1])
        {
            case 'k':
            {
                m_rotatesize = size*1024;
                break;
            }
            case 'm':
            {
                m_rotatesize = size*1024*1024;
                break;
            }
            case 'g':
            {
                m_rotatesize = size*1024*1024*1024;
                break;
            }
            // default 保持 m_rotatesize = 0
        }
    }
}

/// 2h : 2小时,按整点(考虑到按大小滚动,可能不为整点)
/// 2d : 2天
/// 2w : 2周
/// 2m : 2月
void FileChannel::setRotateTime(const string& rtime)
{
    string tmpsize = to_lower(rtime);
    if ( tmpsize.length() > 0)
    {
        m_rtimeCount = atoi(tmpsize.c_str());
        switch( tmpsize[tmpsize.length() - 1])
        {
            case 'h':
            {
                m_rtimeUnit = ROTATE_HOUR;
                break;
            }
            case 'd':
            {
                m_rtimeUnit = ROTATE_DAY;
                break;
            }
            case 'w':
            {
                m_rtimeUnit = ROTATE_WEEK;
                break;
            }
            case 'm':
            {
                m_rtimeUnit = ROTATE_MONTH;
                break;
            }
            // default 保持 m_rtimeUnit = 0
        }
    }
}

string  FileChannel::getPrefix(LogPriority prio,int tid)
{
    string pref = m_preftime + " " + m_prefproc;
    string time;
    if ( m_prefms)
       time = GetTimestamp();
    else
       time = GetTime(m_preftime);

    pref.replace(0,m_preftime.length(),time);
    size_t pos = 0;
    ///   * %p - message priority (fatal, error, warning, info, debug, trace)
    ///   * %P - message process identifier
    ///   * %I - message thread identifier (numeric)
    pos = pref.find("%p");
    if (pos != string::npos)
    {
        const string& level = PriorityString[prio];
        pref.replace(pos,2,level);
    }
    static string pid = int2a(getpid());
    pos = pref.find("%P");
    if (pos != string::npos)
    {
        pref.replace(pos,2,pid);
    }
    pos = pref.find("%I");
    if (pos != string::npos)
    {
        string strtid = int2a(tid);
        pref.replace(pos,2,strtid);
    }
    
    return pref;
}












